## Yield

### Итераторы

Для понимания, что делает yield, необходимо понимать, что такое генераторы.
Генераторам же предшествуют итераторы.

Когда вы создаёте список, вы можете считывать его элементы один за другим — это называется итерацией:

```python
mylist = [1, 2, 3]
for i in mylist :
   print(i)
```

Mylist является итерируемым объектом.

Когда вы создаёте список, используя генераторное выражение, вы создаёте также итератор:

```python
mylist = [x*x for x in range(3)]
for i in mylist :
   print(i)
```

Всё, к чему можно применить конструкцию «for… in...», является итерируемым объектом: списки, строки, файлы… 

Это удобно, потому что можно считывать из них значения сколько потребуется — однако все значения хранятся в памяти, а это не всегда желательно, если у вас много значений.

### Генераторы

Генераторы это тоже итерируемые объекты, но прочитать их можно лишь один раз. Это связано с тем, что они не хранят значения в памяти, а генерируют их на лету:


```python
mygenerator = (x*x for x in range(3))
for i in mygenerator :
  print(i)
```

Всё то же самое, разве что используются круглые скобки вместо квадратных. 

НО: нельзя применить конструкцию `for i in mygenerator` второй раз, так как генератор может быть использован только единожды: он вычисляет `0`, потом забывает про него и вычисляет `1`, завершаяя вычислением `4` — одно за другим.


### Yield

Yield это ключевое слово, которое используется примерно как return — отличие в том, что функция вернёт генератор.


```python

def get_list():
    for x in (1,2,3,10):
        return x


print(get_list())
print(get_list())
print(get_list())



def get_list():
    for x in (1,2,3,10):
        yield x

print(*get_list())

y = get_list()
print(next(y))
print(next(y))
print(next(y))

```

В данном случае  это удобно, если вы знаете, что функция вернёт большой набор значений, который надо будет прочитать только один раз.

```
0 1 4
========================================
0
1
4
```

Чтобы освоить yield, вы должны понимать, что когда вы вызываете функцию, код внутри тела функции не исполняется. Функция только возвращает объект-генератор — немного мудрёно :-)

Ваш код будет вызываться каждый раз, когда for обращается к генератору

Создадим  функцию-генератор последовательности Фиббоначи, которая бы возвращала N первых чисел (включая первые две единицы) с помощью yield.


```python
def generator_fib(N):
    a, b = 0, 1
    for _ in range(N-2):
        yield a
        a, b = b, a + b


f = generator_fib(10)

print (next(f))
print (next(f))
print (next(f))
print(*f)

```

```
1
1
2
3 5 8 13 21 34 55
```

еще пример 
```python
def generator1():
    yield (i for i in range(10))

def generator2():
    return (i for i in range(10))
```

Как правильно вызвать генераторные функции вывести результат?

### chain()

эта функция объединяет указанные итерируемые объекты в один итерируемый объект. 
Она возвращает итератор, в котором последовательно выполняется перебор элементов каждого итерируемого объекта.

Ввод:
```python
import itertools

colors = ['red', 'green', 'blue']
sizes = ['small', 'medium', 'large']

for item in itertools.chain(colors, sizes):
    print(item)
```


Вывод:
```
red
green
blue
small
medium
large
```


### cycle()

эта функция возвращает итератор, который производит элементы конкретного итерируемого объекта многократно и бесконечно.

Ввод:
```python
import itertools

colors = ['red', 'green', 'blue']

for color in itertools.cycle(colors):
    print(color)
```
Вывод:
```
red
green
blue
red
green
blue
red
...
```